import pyaudio
import numpy as np
import logging
from voice_assistant.my_state import app_state
from voice_assistant.com_utils import calculate_rms, run_in_thread

logger = logging.getLogger("VoiceRecorder")

# 音频配置
SAMPLE_RATE = 16000  # 采样率 (Hz)
CHUNK_SIZE = 480     # 每次读取的音频帧大小
FORMAT = pyaudio.paInt16  # 16位音频格式
CHANNELS = 1         # 单声道

# 静音检测配置
SILENCE_THRESHOLD = 350  # 音量阈值，低于此值视为静音（已降低以提高灵敏度）
SILENCE_THRESHOLD_RECORD = 150 # 会议录制（已降低以提高灵敏度）
SILENCE_TIMEOUT = 10.0   # 静音超时时间(秒)，超过此时间自动退出录音状态
# 计算静音超时对应的帧数
MAX_SILENCE_FRAMES = int(SILENCE_TIMEOUT * 1000 / (CHUNK_SIZE * 1000 / SAMPLE_RATE))

class VoiceRecorder:
    """
    语音录制器类，负责从麦克风捕获音频并进行处理。
    
    功能:
    - 持续监听麦克风输入
    - 检测语音活动和静音
    - 将捕获的音频数据放入队列供后续处理
    - 自动在长时间静音后切换到待唤醒状态
    """
    def __init__(self):
        self.is_running = True
        self.audio = None
        self.stream = None
        app_state.recorder = self
        
        try:
            self.audio = pyaudio.PyAudio()
        except Exception as e:
            logger.error(f"无法初始化PyAudio: {str(e)}")
            # 在初始化失败时设置标志，避免后续操作
            self.is_running = False
            return
            
        # 检查是否有可用的输入设备
        device_count = self.audio.get_device_count()
        if device_count == 0:
            logger.error("未检测到任何音频设备")
            self.is_running = False
            self.audio.terminate()
            self.audio = None

    def start(self):
        """启动录音线程"""
        if not self.is_running or not self.audio:
            logger.error("录音器初始化失败，无法启动录音线程")
            return False
            
        run_in_thread(self.record)
        logger.info("录音线程已启动")
        return True
    
    def record(self):
        """录音线程主函数"""
        if self.setup_microphone() is None:
            logger.error("麦克风设置失败，录音线程退出")
            self.is_running = False
            return
            
        # 重置计数器
        app_state.silence_counter = 0 
        audio_buffer = [] # 语音段缓冲区
        consecutive_silence = 0 # 连续静音帧计数
        MAX_CONSECUTIVE_SILENCE = 40 # 连续帧低电平视为语音结束（已增加以提高灵敏度）
        MAX_BUFFER_SIZE = 3000 # 最大缓冲区大小，防止内存溢出
        has_audio = False # 标记缓冲区中是否有有效语音
        
        logger.info("开始持续处理音频...")
        
        while self.is_running:
            try:
                data = self.stream.read(CHUNK_SIZE, exception_on_overflow=False)
                audio_data = np.frombuffer(data, dtype=np.int16)
                
                # 计算RMS值用于静音检测
                rms = calculate_rms(audio_data)
                
                # 静音检测
                audio_buffer.append(data)
                #
                speaking = rms > SILENCE_THRESHOLD_RECORD if app_state.meeting_recording else rms > SILENCE_THRESHOLD
                
                if speaking:
                    consecutive_silence = 0
                    has_audio = True
                    app_state.silence_counter = 0
                else:
                    consecutive_silence += 1
                    app_state.silence_counter += 1
                    
                    #如果达到连续静音帧且缓冲区有数据
                    if consecutive_silence >= MAX_CONSECUTIVE_SILENCE and audio_buffer:
                        if has_audio:
                            app_state.audio_queue.put(b''.join(audio_buffer))
                        audio_buffer = []
                        consecutive_silence = 0
                        has_audio = False
                
                # 检查缓冲区大小，防止内存溢出
                if len(audio_buffer) >= MAX_BUFFER_SIZE:
                    if has_audio:
                        app_state.audio_queue.put(b''.join(audio_buffer))
                        logger.info("音频缓冲区已达到最大大小，提前处理数据")
                    audio_buffer = []
                    consecutive_silence = 0
                    has_audio = False
                
                # 10秒静音超时自动转为待唤醒状态，但会议模式下不自动退出
                if (not app_state.meeting_recording and 
                    app_state.silence_counter > MAX_SILENCE_FRAMES and 
                    not app_state.waiting_for_wake):  # 会议模式下不自动退出
                    logger.info("检测到长时间静音，转为待唤醒状态")
                    app_state.is_recording = False
                    app_state.waiting_for_wake = True
                    app_state.silence_counter = 0
                    # 清空音频缓冲区，避免处理过时的音频数据
                    audio_buffer = []
                    has_audio = False
            except IOError as e:
                # 处理常见的音频IO错误
                logger.error(f"音频IO错误: {str(e)}")
                # 尝试重新初始化流
                if "Input overflowed" in str(e):
                    logger.info("检测到输入溢出，继续处理...")
                    continue
                else:
                    logger.info("尝试重新初始化音频流...")
                    if self.stream:
                        try:
                            self.stream.close()
                        except:
                            pass
                    self.setup_microphone()
                    if not self.stream:
                        logger.error("无法恢复音频流，退出录音循环")
                        break
            except Exception as e:
                logger.error(f"音频处理错误: {str(e)}")
                # 添加短暂暂停以避免CPU占用过高
                import time
                time.sleep(0.1)
    
    def setup_microphone(self):
        """设置麦克风输入"""
        if not self.audio:
            return None
            
        try:
            device_index = self.find_input_device()
            if device_index is None:
                logger.error("错误: 未找到音频设备!")
                return None
        
            self.stream = self.audio.open(
                format=FORMAT,
                channels=CHANNELS,
                rate=SAMPLE_RATE,
                input=True,
                input_device_index=device_index,
                frames_per_buffer=CHUNK_SIZE
            )
            logger.info("麦克风输入已启用")
            return self.stream
        except Exception as e:
            logger.error(f"无法打开麦克风: {str(e)}")
            return None
            
    def find_input_device(self):
        """寻找最佳输入设备"""
        try:
            # 首先尝试获取默认输入设备
            default_device = self.audio.get_default_input_device_info()
            if default_device and default_device["maxInputChannels"] > 0:
                logger.info(f"使用默认输入设备: {default_device['name']}")
                return default_device["index"]
        except Exception as e:
            logger.warning(f"无法获取默认输入设备: {str(e)}")
        
        # 如果无法获取默认设备，则遍历所有设备
        logger.info("搜索可用的输入设备...")
        for i in range(self.audio.get_device_count()):
            try:
                dev_info = self.audio.get_device_info_by_index(i)
                if dev_info["maxInputChannels"] > 0:
                    logger.info(f"选择输入设备: {dev_info['name']}")
                    return i
            except Exception as e:
                logger.warning(f"获取设备 {i} 信息时出错: {str(e)}")
                continue
        
        logger.error("未找到可用的输入设备")
        return None
    
    def stop(self):
        """停止录音和清理资源"""
        self.is_running = False
        try:
            if self.stream:
                self.stream.stop_stream()
                self.stream.close()
                self.stream = None
            if self.audio:
                self.audio.terminate()
                self.audio = None
            logger.info("录音器已停止")
        except Exception as e:
            logger.error(f"停止录音器时出错: {str(e)}")